#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#include <string>
#include "Lock.hpp"
#include "Thread.hpp"
#include <functional>

using namespace std;
using namespace ThreadNS;

static const int number = 5;

template <class T>
class ThreadPool;

template <class T>
struct ThreadData
{
    ThreadData(ThreadPool<T> *T1, const string &S1) : thread(T1), ThreadName(S1)
    {
    }

    ThreadPool<T> *thread;
    string ThreadName;
};

template <class T>
class ThreadPool
{
    typedef function<void *(void *)> func_t; // 线程的任务执行。
private:
    //给子线程执行的函数方法。
    static void *HandlerTask(void *args)  //arg为ThreadData<T>的结构体，里面储存着线程池中所有的成员方法和成员变量，这样就可以不用this指针也可以访问内部方法和成员了。也符合了子线程执行函数方法的函数指针格式
    {
        ThreadData<T> *pt = static_cast<ThreadData<T> *>(args);
        while (true)
        {
            T t;
            //从任务阻塞队列中获取任务。
            pt->thread->pop(&t);
            //t()通过仿函数实现无参调用函数，执行任务。
            cout << "执行一个任务: " << t() << " " << pt->ThreadName << " " << endl;
        }
        return nullptr;
    }

public:
    // 提前初始化互斥量、条件变量，以及管理线程池的数组；但是此时并未产生线程。
    ThreadPool(const int &cap = number, const int &cap_1 = number) : _cap(cap), task_cap(cap_1)
    {
        pthread_cond_init(&_cond, nullptr);
        pthread_mutex_init(&_mutex, nullptr);
        for (int i = 0; i < _cap; i++)
        {
            VTS.push_back(new Thread());
        }
    }

    // 析构：将互斥量、条件变量都destroy掉，将之前new的Thread都delete掉
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        for (int i = 0; i < _cap; i++)
        {
            delete VTS[i];
        }
    }

    // 此时才是循环创建线程。
    void Run()
    {
        for (auto &thread : VTS)
        {
            // 创建一个储存线程池和各线程名的结构体；方便后面直接提取其中的成员函数
            ThreadData<T> *pt = new ThreadData<T>(this, thread->Name());
            // 线程创建
            thread->Start(HandlerTask, pt);
            cout << thread->Name() << "Start..." << endl;
        }
    }

    void push(const T &tmpe)
    {
        {
            //这里使用的是RAII方式的锁，出了该作用域自动解锁。
            LockGuard lock(&_mutex);
            while (_BQ.size() >= _cap)
            {
                //如果任务阻塞队列满了，就进入条件变量进行阻塞等待。（主线程执行）。
                pthread_cond_wait(&_cond, &_mutex);
            }
            _BQ.push(tmpe);
        }
        //走到这里一定代表任务阻塞队列中有了任务，即可以唤醒条件变量中其他还在等待的线程。
        pthread_cond_signal(&_cond);
    }

    void pop(T *tmpe)
    {
        {
            LockGuard lock(&_mutex);
            while (_BQ.size() == 0)
            {
                //如果任务阻塞队列为空，则进入条件变量中等待，（子线程执行）
                pthread_cond_wait(&_cond, &_mutex);
            }
            *tmpe = _BQ.front();
            _BQ.pop();
        }
        pthread_cond_signal(&_cond);
    }

private:
    vector<Thread *> VTS;   // 存放线程的数组
    queue<T> _BQ;           // 任务阻塞队列
    pthread_mutex_t _mutex; // 因为存在临界资源的访问所以需要锁
    pthread_cond_t _cond;   // 相较于生产者消费者模型，此处不需要管理消费者，所以只需要控制生产者即可；
    int _cap;               // 线程池线程数量
    int task_cap;           // 阻塞队列的容量
};